package cn.tedu.charging.order.config;

import cn.tedu.charging.order.mqtt.MqttConsumer;
import cn.tedu.charging.order.mqtt.MqttContext;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * mqtt 客户端配置
 */
@Configuration
public class MqttConfiguration {

    /**
     * 用户名  用户名密码这种修改的，应该单独拿出来统一配置到nacos里，这里为了省事而写死了
     */
    //private String username = "admin";
    private String username = "adddd";

    /**
     * 密码   就是连接emqx用的  这儿的用户名和密码随便起，和登录网页版emqx的账密无关
     */
    //private String password = "public";
    private String password = "pubbb";

    /**
     * 连接地址 tcp协议 端口号 1883  不是常用的http协议
     */
    private String host = "tcp://localhost:1883";

    /**
     * 客户端id, 如果客户端id一致,后加入的会把前面加入的踢掉
     * 游戏账号 只能有一台设备登录
     */
    private String clientId = "order_service_client";

    @Autowired
    private MqttContext mqttContext;

    @Bean
    public MqttClient mqttClient() throws MqttException {

        //通过连接地址和客户端id进行连接
        //客户端id的目的是告诉我们的服务器,谁在链接它
        MqttClient mqttClient = new MqttClient(host, clientId);

        //连接相关的配置，就是设置用户名密码之类的
        MqttConnectOptions conOpts = new MqttConnectOptions();
        //用户名
        conOpts.setUserName(username);
        //密码
        conOpts.setPassword(password.toCharArray());
        //自动重连
        conOpts.setAutomaticReconnect(true);
        //版本号设置
        conOpts.setMqttVersion(MqttConnectOptions.MQTT_VERSION_3_1_1);
        //清理会话session
        conOpts.setCleanSession(true);

        //异步线程，回调方法，设置消费者接收桩的反馈并进行处理保存到数据库
        //6.1 因为mqttClient的发/收connect是基于这些配置，所以要在connect前与MqttConsumer建立关联，后面才能调用MqttConsumer？
        // 在下面的connect连接之前，说清楚回调方法是MqttConsumer类？
        //查看方法源码，setCallback(MqttCallback callback)，参数应该为MqttCallback类。
        // MqttConsumer类实现了MqttCallbackExtended，查看源码，它又继承了MqttCallback类，所以MqttConsumer可以做参数，是其子类。
        //然后老师直接说这儿应该new对象？？？不需要注入容器，所以MqttConsumer去掉Component注解
        //启动时，spring扫描到这个配置类，又扫描到给方法mqttClient构建Bean对象MqttClient，到这一步。没搞懂spring？？？
        //这一步设置一个类用于订阅接收消息，是new的一个类，就意味着不需要spring容器管理，MqttConsumer上不要content注解。即使spring管理了，也还是要在这儿放个MqttConsumer对象
        //把上面的mqttClient传参进去，让MqttConsumer中也有了mqttClient
        //day6上午51分钟50秒，老师说debug下进行，可以看到这儿就是异步进行的？
        // 就是给下面的每一步左面都标红点作为断点，给MqttConsumer的几条log日志也标上红点断点，然后debug启动order
        // 会卡在红点处，然后点停止右面的下一步绿箭头，逐步执行，可以看到顺序是顺着走完return了，才继续走之前没走完的MqttConsumer
        // 就是说执行到setCallback时，connect没有等setCallback有了结果才执行，而是异步进行。return之后才走MqttConsumer
        //启动order后，能看到最后一行才是 MqttConsumer:连接成功，说明是异步。   mqtt本质支持异步的。
        //mqttClient.setCallback(new MqttConsumer(mqttClient));
        mqttClient.setCallback(new MqttConsumer(mqttClient,mqttContext));   //7.1 给MqttConsumer的构造器多增加一个接收参数
        //这时候启动order，启动成功后，就订阅成功了，可以在emqx里看到主题数+1，多了"/topic/start/result"，订阅数+1，客户ID为order_service_client，订阅主题为"/topic/start/result"
        //连接数变为2，1个是mqttfx的随机ID，订阅了java，另一个客户ID就是"order_service_client"

        //给类设置一个属性，也可以通过set方法，比如
        // ChargingDto chargingDto = new ChargingDto();
        // chargingDto.setOrderNo(orderNo);
        // startCharging(chargingDto);  set设置属性，再传类
        //所以上面那行mqttClient.setCallback(new MqttConsumer(mqttClient));可以替换为下面3行，
        // 此时MqttConsumer中就要写个set方法获取外界的mqttClient，不需要用构造方法获取外界mqttClient
        // 两种方法的效果一样，老师说是推荐上面的构造器方式，因为有参构造，new的时候就必须传参，不容易忘。set方法，忘了set传参，也不会报错，不好找到问题。
//        MqttConsumer mqttConsumer = new MqttConsumer();
//        mqttConsumer.setMqttClient(mqttClient);
//        mqttClient.setCallback(mqttConsumer);


        //基于上面的配置conOpts进行连接
        mqttClient.connect(conOpts);

        return mqttClient;

    }


}
